﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Threading;

////////////////////////////////////////////////////////////////////////////////////////////////////
// namespace: Dimok
//
// summary:	.
////////////////////////////////////////////////////////////////////////////////////////////////////

namespace Dimok {

	/// <summary>
	/// Provides a base class for implementing models that encapsulate
	/// data and behavior that is independent of the presentation.
	/// </summary>
	public abstract class ModelBase : INotifyPropertyChanged {

		private static readonly Dictionary<string, PropertyChangedEventArgs> _eventArgsMap =
				new Dictionary<string, PropertyChangedEventArgs>();

		private PropertyChangedEventHandler _propChangedHandler;
		private SynchronizationContext _syncContext;

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Initializes an instance of a Model. </summary>
		///
		/// <remarks>	Dimon, 11/23/2010. </remarks>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		protected ModelBase() {
			_syncContext = SynchronizationContext.Current;
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Gets an event arguments. </summary>
		///
		/// <param name="propertyName">	The property that has changed. </param>
		///
		/// <returns>	The event arguments. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		private static PropertyChangedEventArgs GetEventArgs(string propertyName) {
			PropertyChangedEventArgs pe = null;
			if (_eventArgsMap.TryGetValue(propertyName, out pe) == false) {
				pe = new PropertyChangedEventArgs(propertyName);
				_eventArgsMap[propertyName] = pe;
			}

			return pe;
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	
		/// Raises a change notification event to signal a change in the specified property's value. 
		/// </summary>
		///
		/// <param name="propertyName">	The property that has changed. </param>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		protected void OnPropertyChanged(string propertyName) {
			if (String.IsNullOrEmpty(propertyName)) {
				throw new ArgumentNullException("propertyName");
			}

			if (_propChangedHandler == null) {
				return;
			}

			if (_syncContext != null) {
				_syncContext.Post(delegate(object state) {
					if (_propChangedHandler != null) {
						_propChangedHandler(this, GetEventArgs(propertyName));
					}
				}, null);
			} else {
				_propChangedHandler(this, GetEventArgs(propertyName));
			}
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Raises a change notification event to signal a change in the specified properties. </summary>
		///
		/// <param name="propertyNames">	The properties that have changed. </param>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		protected void OnPropertyChanged(params string[] propertyNames) {
			if ((propertyNames == null) || (propertyNames.Length == 0)) {
				throw new ArgumentNullException("propertyNames");
			}
			if (_propChangedHandler == null) {
				return;
			}

			if (_syncContext != null) {
				_syncContext.Post(delegate(object state) {
					if (_propChangedHandler != null) {
						foreach (string propertyName in propertyNames) {
							_propChangedHandler(this, GetEventArgs(propertyName));
						}
					}
				}, null);
			} else {
				foreach (string propertyName in propertyNames) {
					_propChangedHandler(this, GetEventArgs(propertyName));
				}
			}
		}

		#region Implementation of INotifyPropertyChanged

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	 the property changed. </summary>
		///
		/// <value>	The property changed. </value>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged {
			add {
				_propChangedHandler = (PropertyChangedEventHandler)Delegate.Combine(_propChangedHandler, value);
			}
			remove {
				if (_propChangedHandler != null) {
					_propChangedHandler = (PropertyChangedEventHandler)Delegate.Remove(_propChangedHandler, value);
				}
			}
		}
		#endregion
	}
}
